サーバレース環境で「カオスエンジニアリング」が実装できる aws-lambda-chaos-injection を試してみた
こんにちは。コンサル部のテウです。
カオスエンジニアリングの主なターゲットとしては Server-based のことが多いですが、Serverless 環境でのカオスエンジニアリングが簡単にできる python ベースのライブラリーが公開されています。今回はこのライブラリーを試してみたのでの紹介記事となります。
目次
Chaos Lambda (Chaos Injection for AWS Lambda) とは
先日AWSJで開催された Chaos Engineering Recap 2019 のセッションの一つで紹介されたこの Chaos Lambda というライブラリーはAWSで Principal Evangelist として働いている Adrian Hornsbyさんが開発したライブラリーです。Githubで公開されたライブラリーですが、Lambda環境で簡単にカオスエンジニアリングが実装できる機能が私にとっては非常に面白かったです。まだ正式なバージョンではなく、alphaステージのバージョンですが、簡単なユーズケースに使用することは可能なレベルだと思います。
英語ですが、こちらのページに詳細な説明とサンプルコードが記載されていて、そのまま試してみることが可能です。
やってみた
Serverless v1.57
Serverless frameowrkを使って、Chaos Lambdaを試してみました。先ずはプロジェクトフォルダーを作ります。私のServerless frameworkのバージョンは下の通りです。
$ serverless version Framework Core: 1.57.0 Plugin: 3.2.3 SDK: 2.2.1 Components Core: 1.1.2 Components CLI: 1.4.0 ╭───────────────────────────────────────╮ │ │ │ Update available 1.57.0 → 1.58.0 │ │ Run npm i -g serverless to update │ │ │ ╰───────────────────────────────────────╯
プロジェクト生成
早速、以下の順番で作業環境を作ります。
$ mkdir my-first-chaos-lambda $ cd my-first-chaos-lambda $ serverless create --template=aws-python3 $ virtualenv venv $ source venv/bin/activate $ pip install chaos-lambda $ yarn add serverless-python-requirements (あるいは npm install serverless-python-requirements --save) $ pip freeze > requirements.txt
SSM Parameter Storeに設定ファイルを保存
コードを作成する前にSSM Parameter Storeに設定ファイルを保存しておきました。
aws ssm put-parameter --region ap-northeast-1 --name chaoslambda.myconfig --type String --overwrite --value "{ \"delay\": 400, \"isEnabled\": true, \"error_code\": 404, \"exception_msg\": \"失敗したぞ!!\", \"rate\": 1 }"
実行後に以下のメッセージが表示されたら成功です。
{ "Version": 1 }
コード作成
次は handler.py を以下のように修正しました。
import os from chaos_lambda import inject_delay, inject_exception, inject_statuscode # this should be set as a Lambda environment variable os.environ['CHAOS_PARAM'] = 'chaoslambda.myconfig' @inject_exception def handler_with_exception(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_exception(exception_type=TypeError, exception_msg='foobar') def handler_with_exception_arg(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_exception(exception_type=ValueError) def handler_with_exception_arg_2(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_statuscode def handler_with_statuscode(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_statuscode(error_code=400) def handler_with_statuscode_arg(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_delay def handler_with_delay(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_delay(delay=1000) def handler_with_delay_arg(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } @inject_delay(delay=0) def handler_with_delay_zero(event, context): return { 'statusCode': 200, 'body': 'Hello from Lambda!' } if __name__ == '__main__': handler_with_exception('foo', 'bar')
Python のデコレーションであり @inject_exception と @inject_delay の部分を追加するだけでカオスエンジニアリングが可能に見えます。ローカル環境で実行してみます。最後の handler_with_exception('foo', 'bar') の部分が実行されます。
コードをローカル環境で実行
$ python handler.py
すると、以下のようなエラーメッセージが出力されます。
先SSM Parameter Storeに保存したエラーメッセージが表示されました。 はい、意図的に失敗を作り、その時の対応ができるかどうかをこんなに簡単なコードで確認できますね。
次のステップに進む前に、5行の os.environ['CHAOS_PARAM'] = 'chaoslambda.myconfig' はコメント処理します。Lambdaの環境変数として渡すためです。
os.environ['CHAOS_PARAM'] = 'chaoslambda.myconfig'
のところを
# os.environ['CHAOS_PARAM'] = 'chaoslambda.myconfig'
に変更します。
serverless.yml
次は作成したコードをデプロイするために serverless.yml を以下のように修正しました。
service: my-first-chaos-lambda provider: name: aws runtime: python3.7 stage: dev region: ap-northeast-1 iamRoleStatements: - Effect: "Allow" Action: - "ssm:DescribeParameters" Resource: "*" - Effect: "Allow" Action: - "ssm:GetParameters" - "ssm:GetParameter" Resource: "arn:aws:ssm:ap-northeast-1:*:parameter/chaoslambda.myconfig" functions: hello: handler: handler.handler_with_exception events: - http: path: handler_with_exception method: get environment: CHAOS_PARAM: "chaoslambda.myconfig" plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: true
デプロイ
デプロイの瞬間はいつもワクワクしてますね。はい、以下のコマンドでデプロイします。
$ sls deploy -v (あるいは serverless deploy -v)
デプロイログの最後に下のようなサービス情報が表示されたらデプロイは成功です。
結果の確認
デプロイが完了したら Service Endpoint のところの URLに接続してみます。私の場合は
https://mwpu2io1ga.execute-api.ap-northeast-1.amazonaws.com/dev/handler_with_exception
でした。Chrome で接続すると以下のようなエラーが出てきます。
{"message": "Internal server error"}
何が悪いのかすぐ不安になってしまうんですが、実はこれは想定通りですね。ですが、CloudWatch Logs にてログを見てから本当に成功か失敗かが判断できるはずです。 CloudWatch Logsをみると下のようにログが出力されていました。
失敗したぞ!!という私が書いた設定ファイルのエラーメッセージが出力されたので、今回のデプロイは成功でした。
これで何ができるの?
そうですね。一番大事な質問だと思います。
Server-based の環境ですと、比較的にカオスエンジニアリングを実装しやすいと思います。サーバのインスタンスを消したり、停止したり、CPU使用率に負荷をかけたりいろんなシナリオが実装可能な Server-based 環境とは異なり、Serverless の環境ですと、サーバというコンポーネントが無く、停止やリソースに負荷をかけるなどの作業がかなり難しくなります。(不可能な作業もありますよね)
ですが、本記事で紹介された Chaos Lambda みたいなツールを活用すれば、サーバレス環境でもそういういろんな障害を作ることが楽になります。
AWSのCTOである Werner Vogelsさんは皆さんのご存知の通り “Everything fails all the time” (全ては常に失敗している)という言葉を残しました。はい、AWSも他のクラウドもいつかは障害を起こします。その時に備えて障害の範囲を最小化する仕組みを考えべきだと思います。
カオスエンジニアリングはこの考えのパターンであり、文化でもあると思います。サーバレスのユーズケースがどんどん増えていくことを実感していますが、サーバレス環境での障害が起きた時の対策に関する考えや資料はあまり少ない気がします。私はこのようなサーバレス環境でカオスエンジニアリングが実装可能なツール等が増えていくことを望んでいます。
最後に
Re:Invent がもうD-4 ですね。私はRe:Invent途中に Chaos Engineering Meetup @ re:Invent というイベントで弊社のメンバーと一緒に参加します。ミットアップの記事も書くつもりですので、お楽しみにしてください!
それでは、以上、コンサル部のテウでした。